#pragma once

#include <sys/epoll.h>
#include <string.h>
#include <set>
#include "Log.hpp"

using namespace std;

const static int defaultepfd = -1;
const static int defaultsize = 1024;

class Epoller
{
public:
    Epoller()
        : _epfd(defaultepfd)
    {
    }

    // 初始化Epoller
    void InitEpoller()
    {
        _epfd = epoll_create(defaultsize);

        if (_epfd == defaultepfd)
        {
            lg.LogMessage(Fatal, "epoll_create fail , error: %s , errno : %d\n", strerror(errno), errno);
            exit(-1);
        }
        else
        {
            lg.LogMessage(Info, "epoll_create success , epfd : %d\n", _epfd);
        }
    }

    // 向对应文件描述符中添加事件关心
    int AddEvent(int sockfd, uint32_t events)
    {
        fd_list.insert(sockfd); // for test

        struct epoll_event event;

        event.events = events;
        event.data.fd = sockfd;

        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &event);

        if (n == -1)
        {
            lg.LogMessage(Fatal, "add %d event fail , error: %s , errno : %d\n", sockfd, strerror(errno), errno);
        }
        else
        {
            lg.LogMessage(Info, "add %d event success\n", sockfd);
        }

        return n;
    }

    // 等待事件就绪
    int Wait(struct epoll_event *events, int maxevents, int timeout)
    {
        int n = epoll_wait(_epfd, events, maxevents, timeout);

        if (n == -1)
        {
            lg.LogMessage(Error, "wait %d fail , error: %s , errno : %d\n", events->data.fd, strerror(errno), errno);
        }
        else
        {
            lg.LogMessage(Info, "wait %d success , events %d \n", events->data.fd, events->events);
        }

        return n;
    }

    // 用于测试，输出目前所有连接的文件描述符
    void DebugFdList() // for test
    {
        std::cout << "fd list is : ";
        for (auto &fd : fd_list)
        {
            std::cout << fd << " ";
        }
        std::cout << std::endl;
    }

    // 修改对应文件描述符的事件关心
    void ModEvents(int sockfd, uint32_t events)
    {
        struct epoll_event event;

        event.events = events;
        event.data.fd = sockfd;

        int n = epoll_ctl(_epfd, EPOLL_CTL_MOD, sockfd, &event);
        if (n < 0)
        {
            lg.LogMessage(Error, "epoll_ctl mod fail, error : %s , errno : %d\n", strerror(errno), errno);
        }
    }

    // 删除对应文件描述符的事件关心
    void DelEvents(int sockfd)
    {
        fd_list.erase(sockfd); // for test

        int n = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
        if (n < 0)
        {
            lg.LogMessage(Error, "epoll_ctl del fail, error : %s , errno : %d\n", strerror(errno), errno);
        }
    }

    ~Epoller()
    {
        if (_epfd >= 0)
        {
            close(_epfd);
        }
        lg.LogMessage(Info, "epoll close success\n");
    }

private:
    int _epfd;
    set<int> fd_list;
};